package com.neurotech.pcc.other

import android.os.Handler
import android.os.Looper
import android.util.Log
import com.neurotech.pcc.dummy.AreaDevice
import com.neurotech.pcc.dummy.AreaDeviceScanListener
import com.neurotech.pcc.dummy.PingCallback
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.*


/**
 * Created by shenhua on 2018/10/15.
 * @author shenhua
 *         Email shenhuanet@126.com
 */
object NetworkUtils {

    private val handler = Handler(Looper.getMainLooper())

    fun scan(areaDeviceScanListener: AreaDeviceScanListener) {
        ThreadPool.get().thread().execute {
            try {
                val locAddress = getLocAddress()
                val addressIndex = getLocAddressIndex(locAddress)

                val packet = DatagramPacket(ByteArray(0), 0, 0)
                var socket = DatagramSocket()

                for (position in 2..255) {
                    packet.address = InetAddress.getByName("$addressIndex$position")
                    socket.send(packet)
                    // 分两段发送数据包，时间节省到100ms内，一次性发送数据包则需要3000ms
                    if (position == 125) {
                        socket.close()
                        socket = DatagramSocket()
                    }
                }
                socket.close()
                execCatForArp(locAddress, areaDeviceScanListener)
            } catch (e: Exception) {

            }
        }
    }

    @Deprecated("un used")
    fun wake(ip: String, mac: String, port: String) {
        ThreadPool.get().thread().execute {
            val macBytes = getMacBytes(mac)
            val bytes = ByteArray(6 + 16 * macBytes.size)
            for (i in 0..5) {
                bytes[i] = 0xFF.toByte()
            }
            for (i in 6 until bytes.size step 6) {
                System.arraycopy(macBytes, 0, bytes, i, macBytes.size)
            }

            val destination = InetSocketAddress(ip, port.toInt())
            val packet = DatagramPacket(bytes, bytes.size, destination)
            val socket = DatagramSocket()
            socket.broadcast = true
            try {
                socket.send(packet)
            } catch (e: IOException) {
                // ignore
            } finally {
                socket.close()
            }
        }
    }

    fun wake(mac: String, port: String) {
        ThreadPool.get().thread().execute {
            val macBytes = getMacBytes(mac)
            Log.d("shenhuaLog -- ${NetworkUtils::class.java.simpleName}", "wake: ${macBytes}")
            val bytes = ByteArray(6 + 16 * macBytes.size)
            for (i in 0..5) {
                bytes[i] = 0xFF.toByte()
            }
            for (i in 6 until bytes.size step 6) {
                System.arraycopy(macBytes, 0, bytes, i, macBytes.size)
            }

            val destination = InetSocketAddress("255.255.255.255", port.toInt())
            val packet = DatagramPacket(bytes, bytes.size, destination)
            val socket = DatagramSocket()
            socket.broadcast = true
            try {
                socket.send(packet)
            } catch (e: IOException) {
                // ignore
            } finally {
                socket.close()
            }
        }
    }

    fun ping(ip: String?, pingCallback: PingCallback) {
        if (ip == null) {
            return
        }
        val exec = Runtime.getRuntime().exec("ping -c 1 -w 2 $ip")
        val result = exec.waitFor()
        pingCallback.result(result == 0)
    }

    private fun execCatForArp(localIp: String, areaDeviceScanListener: AreaDeviceScanListener) {
        val exec = Runtime.getRuntime().exec("cat proc/net/arp")
        BufferedReader(InputStreamReader(exec.inputStream)).use {
            var line: String
            while (true) {
                line = it.readLine() ?: break
                if (!line.contains("00:00:00:00:00:00") && !line.contains("IP")) {
                    val split = line.split(Regex("\\s+"))
                    Thread.sleep(100)
                    handler.post { areaDeviceScanListener.onScan(AreaDevice(split[0], split[3].toUpperCase(), split[0] == localIp)) }
                }
            }
        }
    }

    private fun getLocAddress(): String {
        try {
            val en = NetworkInterface.getNetworkInterfaces()
            while (en.hasMoreElements()) {
                val networks = en.nextElement()
                val address = networks.inetAddresses
                while (address.hasMoreElements()) {
                    val ip = address.nextElement()
                    if (!ip.isLoopbackAddress && ip is Inet4Address) {
                        return ip.hostAddress
                    }
                }
            }
        } catch (e: SocketException) {
            e.printStackTrace()
        }
        return ""
    }

    /**
     * 获取ip所在网段
     * @param locAddress 本机
     */
    private fun getLocAddressIndex(locAddress: String): String? {
        return if (locAddress != "") {
            locAddress.substring(0, locAddress.lastIndexOf(".") + 1)
        } else null
    }

    private fun getMacBytes(mac: String): ByteArray {
        val bytes = ByteArray(6)
        val hex = mac.split(":")
        if (hex.size != 6) {
            throw IllegalArgumentException("Invalid MAC address.")
        }
        try {
            for (i in 0..5) {
                bytes[i] = Integer.parseInt(hex[i], 16).toByte()
            }
        } catch (e: NumberFormatException) {
            throw IllegalArgumentException("Invalid hex digit in MAC address.")
        }
        return bytes
    }

    private fun bytesToHexString(src: ByteArray?): String? {
        if (src == null || src.isEmpty()) {
            return null
        }
        val stringBuilder = StringBuilder("")
        for (i in 0 until src.size) {
            val v = src[i].toInt() and 0xFF
            val hv = Integer.toHexString(v)
            if (hv.length < 2) {
                stringBuilder.append(0)
            }
            stringBuilder.append(hv)
        }
        return stringBuilder.toString().toUpperCase()
    }

    /**
     * 获取广播地址
     */
    fun getBroadcastAddress(subnet: String, ip: String): String {
        val ips = ip.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        val subnets = subnet.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        val sb = StringBuilder()
        for (i in ips.indices) {
            ips[i] = (Integer.parseInt(subnets[i]).inv() or Integer.parseInt(ips[i])).toString()
            sb.append(turnToStr(Integer.parseInt(ips[i])))
            if (i != ips.size - 1) {
                sb.append(".")
            }
        }
        return turnToIp(sb.toString())
    }

    private fun turnToStr(num: Int): String {
        var str: StringBuilder
        str = StringBuilder(Integer.toBinaryString(num))
        val len = 8 - str.length
        for (i in 0 until len) {
            str.insert(0, "0")
        }
        if (len < 0) {
            str = StringBuilder(str.substring(24, 32))
        }
        return str.toString()
    }

    /**
     * 转换成Str
     */
    private fun turnToIp(str: String): String {
        val ips = str.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
        val sb = StringBuilder()
        for (ip in ips) {
            sb.append(turnToInt(ip))
            sb.append(".")
        }
        sb.deleteCharAt(sb.length - 1)
        return sb.toString()
    }

    /**
     * 转换成int
     */
    private fun turnToInt(str: String): Int {
        var total = 0
        var top = str.length
        for (i in 0 until str.length) {
            val h = str[i].toString()
            top--
            total += Math.pow(2.0, top.toDouble()).toInt() * Integer.parseInt(h)
        }
        return total
    }

}